/*
 * OGL01Shape3D.cpp: 3D Shapes
 */
#include <GL/glut.h>  // GLUT, include glu.h and gl.h
#include "math.h"
/* Global variables */
char title[] = "3D Shapes";

/* Initialize OpenGL Graphics */
void initGL() {
    glClearDepth(1.0f);                   // Set background depth to farthest
    glShadeModel(GL_SMOOTH);   // Enable smooth shading
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);  // Nice perspective corrections
    // цвет фона
    glClearColor (0.1, 0.1, 0.1, 0.0);
    // рассчет освещения
    glEnable(GL_LIGHTING);
    // двухсторонний расчет освещения
    glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
    // автоматическое приведение нормалей к
    // единичной длине
    glEnable(GL_NORMALIZE);

}

const float cube_size = 1.0;

void quad() {
    const float cs = cube_size/2;
    glBegin(GL_QUADS);                // Begin drawing the color cube with 6 quads
    glNormal3b(0, 0, -1);
    glVertex3f( cs, -cs, 0);
    glVertex3f(-cs, -cs, 0 );
    glVertex3f(-cs, cs,  0);
    glVertex3f( cs, cs,  0);
    glEnd();
}


void cube() {
    const float cs = cube_size/2;
    glPushMatrix();
    glTranslatef(0, 0, -cs);
    quad();
    glPopMatrix();
    glPushMatrix();
    glRotatef(180, 1, 0, 0);
    //glTranslatef(0, 0, cs);
    quad();
    glPopMatrix();
    glPushMatrix();
    glRotatef(90, 1, 0, 0);
    glTranslatef(0, 0, cs);
    quad();
    glPopMatrix();
    glPushMatrix();
    glRotatef(90, -1, 0, 0);
    glTranslatef(0, 0, cs);
    quad();
    glPopMatrix();
}


void _cube() {
    const float cs = cube_size/2;
    //glColor3f(0.5f, 0.5f, 0.5f);     // Green
    // Top face (y = 1.0f)
    // Define vertices in counter-clockwise (CCW) order with normal pointing out

/*
    glBegin(GL_QUADS);                // Begin drawing the color cube with 6 quads
    glNormal3b(0, 1, 0);
    glVertex3f( cs, cs, -cs);
    glVertex3f(-cs, cs, -cs);
    glVertex3f(-cs, cs,  cs);
    glVertex3f( cs, cs,  cs);
    glEnd();
*/



    // Bottom face (y = -1.0f)
    glBegin(GL_QUADS);                // Begin drawing the color cube with 6 quads
    glNormal3b(0, -1, 0);
    glVertex3f( cs, -cs,  cs);
    glVertex3f(-cs, -cs,  cs);
    glVertex3f(-cs, -cs, -cs);
    glVertex3f( cs, -cs, -cs);
    glEnd();


    // Left face (x = -cs)
    glBegin(GL_QUADS);                // Begin drawing the color cube with 6 quads
    glNormal3b(-1, 0, 0);
    glVertex3f(-cs,  cs,  cs);
    glVertex3f(-cs,  cs, -cs);
    glVertex3f(-cs, -cs, -cs);
    glVertex3f(-cs, -cs,  cs);
    glEnd();


    // Right face (x = cs)
    glBegin(GL_QUADS);                // Begin drawing the color cube with 6 quads
    glNormal3b(1, 0, 0);
    glVertex3f(cs,  cs, -cs);
    glVertex3f(cs,  cs,  cs);
    glVertex3f(cs, -cs,  cs);
    glVertex3f(cs, -cs, -cs);
    glEnd();

    // Back face (z = -cs)
    glBegin(GL_QUADS);                // Begin drawing the color cube with 6 quads
    glNormal3b(0, 0, -1);
    glVertex3f( cs, -cs, -cs);
    glVertex3f(-cs, -cs, -cs);
    glVertex3f(-cs,  cs, -cs);
    glVertex3f( cs,  cs, -cs);
    glEnd();

/*
    // Front face  (z = 1.0f)
    glNormal3b(0, 0, -1);
    glVertex3f( cs,  cs, cs);
    glVertex3f(-cs,  cs, cs);
    glVertex3f(-cs, -cs, cs);
    glVertex3f( cs, -cs, cs);



*/



    glEnd();  // End of drawing color-cube
}

void row() {
    glPushMatrix();
    glTranslatef(-cube_size * 1.1, 0, 0);
    cube();
    glPopMatrix();
    glPushMatrix();
    cube();
    glPopMatrix();
    glPushMatrix();
    glTranslatef(cube_size * 1.1, 0, 0);
    cube();
    glPopMatrix();
}

void layer() {
    glPushMatrix();
    glTranslatef(0, -cube_size * 1.1, 0);
    row();
    glPopMatrix();
    glPushMatrix();
    row();
    glPopMatrix();
    glPushMatrix();
    glTranslatef(0, cube_size * 1.1, 0);
    row();
    glPopMatrix();
}

void cube_set() {
    glPushMatrix();
    glTranslatef(0, 0, -cube_size * 1.1);
    layer();
    glPopMatrix();
    glPushMatrix();
    layer();
    glPopMatrix();
    glPushMatrix();
    glTranslatef(0, 0, cube_size * 1.1);
    layer();
    glPopMatrix();
}

void light(int light_sample) {
// очищаем буфер кадра и глубины
    //glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    // свойства материала
    GLfloat material_diffuse[] = {0.1, 0.1, 0.1, 0.0001};
    glMaterialfv(GL_BACK, GL_DIFFUSE, material_diffuse);
    // установка источников света
    if (light_sample == 1){
        // направленный источник света
        GLfloat light0_diffuse[] = {0.4, 0.7, 0.2};
        GLfloat light0_direction[] = {0.0, 0.0, 1.0, 0.0};
        glEnable(GL_LIGHT0);
        glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
        glLightfv(GL_LIGHT0, GL_POSITION, light0_direction);
    }

    if (light_sample == 2){
        // точечный источник света
        // убывание интенсивности с расстоянием
        // отключено (по умолчанию)
        GLfloat light1_diffuse[] = {0.3, 0.3, 0.3};
        GLfloat light1_position[] = {0.1, 0, 0, -10.0};
        glEnable(GL_LIGHT1);
        glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_diffuse);
        glLightfv(GL_LIGHT1, GL_POSITION, light1_position);
        glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, 0);
        glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.0005);
        glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, 0);
    }
    if (light_sample == 3) {
        // точечный источник света
        // убывание интенсивности с расстоянием
        // задано функцией f(d) = 1.0 / (0.4 * d * d + 0.2 * d)
        GLfloat light2_diffuse[] = {0.4, 0.2, 0.2};
        GLfloat light2_position[] = {0.0, 0.0, 1.0, 1.0};
        glEnable(GL_LIGHT2);
        glLightfv(GL_LIGHT2, GL_DIFFUSE, light2_diffuse);
        glLightfv(GL_LIGHT2, GL_POSITION, light2_position);
        glLightf(GL_LIGHT2, GL_CONSTANT_ATTENUATION, 0.0);
        glLightf(GL_LIGHT2, GL_LINEAR_ATTENUATION, 0.2);
        glLightf(GL_LIGHT2, GL_QUADRATIC_ATTENUATION, 0.4);
    }
    if (light_sample == 4) {
        // прожектор
        // убывание интенсивности с расстоянием
        // отключено (по умолчанию)
        // половина угла при вершине 30 градусов
        // направление на центр плоскости
        GLfloat light3_diffuse[] = {0.4, 0.7, 0.2};
        GLfloat light3_position[] = {0.0, 0.0, 1.0, 1.0};
        GLfloat light3_spot_direction[] = {0.0, 0.0, -1.0};
        glEnable(GL_LIGHT3);
        glLightfv(GL_LIGHT3, GL_DIFFUSE, light3_diffuse);
        glLightfv(GL_LIGHT3, GL_POSITION, light3_position);
        glLightf(GL_LIGHT3, GL_SPOT_CUTOFF, 30);
        glLightfv(GL_LIGHT3, GL_SPOT_DIRECTION, light3_spot_direction);
    }
    if (light_sample == 5) {
        // прожектор
        // убывание интенсивности с расстоянием
        // отключено (по умолчанию)
        // половина угла при вершине 30 градусов
        // направление на центр плоскости
        // включен рассчет убывания интенсивности для прожектора
        GLfloat light4_diffuse[] = {0.4, 0.7, 0.2};
        GLfloat light4_position[] = {0.0, 0.0, 1.0, 1.0};
        GLfloat light4_spot_direction[] = {0.0, 0.0, -1.0};
        glEnable(GL_LIGHT4);
        glLightfv(GL_LIGHT4, GL_DIFFUSE, light4_diffuse);
        glLightfv(GL_LIGHT4, GL_POSITION, light4_position);
        glLightf(GL_LIGHT4, GL_SPOT_CUTOFF, 30);
        glLightfv(GL_LIGHT4, GL_SPOT_DIRECTION, light4_spot_direction);
        glLightf(GL_LIGHT4, GL_SPOT_EXPONENT, 15.0);
    }

    if (light_sample == 6) {
        // несколько источников света
        GLfloat light5_diffuse[] = {1.0, 0.0, 0.0};
        GLfloat light5_position[] = {0.5 * cos(0.0), 0.5 * sin(0.0), 1.0, 1.0};
        glEnable(GL_LIGHT5);
        glLightfv(GL_LIGHT5, GL_DIFFUSE, light5_diffuse);
        glLightfv(GL_LIGHT5, GL_POSITION, light5_position);
        glLightf(GL_LIGHT5, GL_CONSTANT_ATTENUATION, 0.0);
        glLightf(GL_LIGHT5, GL_LINEAR_ATTENUATION, 0.4);
        glLightf(GL_LIGHT5, GL_QUADRATIC_ATTENUATION, 0.8);
        GLfloat light6_diffuse[] = {0.0, 1.0, 0.0};
        GLfloat light6_position[] = {0.5 * cos(2 * 3.14 / 3), 0.5 * sin(2 * 3.14 / 3), 1.0, 1.0};
        glEnable(GL_LIGHT6);
        glLightfv(GL_LIGHT6, GL_DIFFUSE, light6_diffuse);
        glLightfv(GL_LIGHT6, GL_POSITION, light6_position);
        glLightf(GL_LIGHT6, GL_CONSTANT_ATTENUATION, 0.0);
        glLightf(GL_LIGHT6, GL_LINEAR_ATTENUATION, 0.4);
        glLightf(GL_LIGHT6, GL_QUADRATIC_ATTENUATION, 0.8);
        GLfloat light7_diffuse[] = {0.0, 0.0, 1.0};
        GLfloat light7_position[] = {0.5 * cos(4 * 3.14 / 3), 0.5 * sin(4 * 3.14 / 3), 1.0, 1.0};
        glEnable(GL_LIGHT7);
        glLightfv(GL_LIGHT7, GL_DIFFUSE, light7_diffuse);
        glLightfv(GL_LIGHT7, GL_POSITION, light7_position);
        glLightf(GL_LIGHT7, GL_CONSTANT_ATTENUATION, 0.0);
        glLightf(GL_LIGHT7, GL_LINEAR_ATTENUATION, 0.4);
        glLightf(GL_LIGHT7, GL_QUADRATIC_ATTENUATION, 0.8);
    }
}


/* Handler for window-repaint event. Called back when the window first appears and
   whenever the window needs to be re-painted. */
void display() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear color and depth buffers
    glMatrixMode(GL_MODELVIEW);     // To operate on model-view matrix


    // автоматическое приведение нормалей к

    // единичной длине

    light(2);
    // Render a color-cube consisting of 6 quads with different colors
    glLoadIdentity();                 // Reset the model-view matrix
    glTranslatef(0.0f, 0.0f, -4.0f);  // Move right and into the screen
    glRotatef(30, 0.1f, 0.1f, -0.1f);  // Move right and into the screen

    cube();
    // Render a pyramid consists of 4 triangles
    glutSwapBuffers();  // Swap the front and back frame buffers (double buffering)
}

/* Handler for window re-size event. Called back when the window first appears and
   whenever the window is re-sized with its new width and height */
void reshape(GLsizei width, GLsizei height) {  // GLsizei for non-negative integer
    // Compute aspect ratio of the new window
    if (height == 0) height = 1;                // To prevent divide by 0
    GLfloat aspect = (GLfloat)width / (GLfloat)height;

    // Set the viewport to cover the new window
    glViewport(0, 0, width, height);

    // Set the aspect ratio of the clipping volume to match the viewport
    glMatrixMode(GL_PROJECTION);  // To operate on the Projection matrix
    glLoadIdentity();             // Reset
    // Enable perspective projection with fovy, aspect, zNear and zFar
    gluPerspective(45.0f, aspect, 0.1f, 100.0f);
}

/* Main function: GLUT runs as a console application starting at main() */
int _main(int argc, char** argv) {
    glutInit(&argc, argv);            // Initialize GLUT
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH); // Enable double buffered mode
    glutInitWindowSize(640, 1000);   // Set the window's initial width & height
    glutInitWindowPosition(50, 50); // Position the window's initial top-left corner
    glutCreateWindow(title);          // Create window with the given title
    glutDisplayFunc(display);       // Register callback handler for window re-paint event
    glutReshapeFunc(reshape);       // Register callback handler for window re-size event
    initGL();                       // Our own OpenGL initialization
    glutMainLoop();                 // Enter the infinite event-processing loop
    return 0;
}